/*
   Copyright [2013] [szhu1@umbc.edu]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
package szhu.hcc.umbc.crowdsourcing.quality.core;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * An improved version to try address the gaming issues.
 *
 * @author szhu1@umbc.edu
 */
public class QualityJudgingGamingCoreImpl extends QualityJudgingCoreImpl implements QualityJudgingGamingCore {

    protected Map<String, Integer> workerBlackList;
    public QualityJudgingGamingCoreImpl(List<RawData> Logs) {
        super(Logs);
    }


    protected void saveWorkerBlackListToMap(String workerId) {
        if (workerBlackList.containsKey(workerId)) {
            workerBlackList.put(workerId, workerBlackList.get(workerId) + 1);
        } else {
            workerBlackList.put(workerId, 1);
        }
    }

    protected void scanMalicious(List<Integer> trainingTaskid) {
        for (int i = 0; i < rawLogs.size(); i += MAX_WORKER_PER_TASK) {
            if (trainingTaskid.contains(i / MAX_WORKER_PER_TASK)) {
                for (int j = 0; j < MAX_WORKER_PER_TASK; j++) {
                    RawData rawData = rawLogs.get(i + j);
                    if (rawData.quality < 0) {
                        saveWorkerBlackListToMap(rawData.workerId);
                    }
                }
            }
        }
        for (int i = 0; i < rawLogs.size(); i += MAX_WORKER_PER_TASK) {
            if (trainingTaskid.contains(i / MAX_WORKER_PER_TASK)) {
                for (int j = 0; j < MAX_WORKER_PER_TASK; j++) {
                    if (inWorkerBlackList(rawLogs.get(i + j).workerId)) {
                        int otherIndex = (j == 0) ? 1 : 0;
                        rawLogs.set(i + j, rawLogs.get(i + otherIndex));
//                        System.out.println("Exclude user: " + rawLogs.get(i + j).workerId);
//                        System.out.println("Exclude training task: " + (i / MAX_WORKER_PER_TASK));
//                        int removeIndex = trainingTaskid.indexOf(i / MAX_WORKER_PER_TASK);
//                        if (removeIndex >= 0) {
//                            trainingTaskid.remove(removeIndex);
//                        }
                        break;
                    }
                }
            }
        }
    }

    /**
     * To evaluate the recognition rate for simulated "gaming" tasks.
     */
    @Override
    public double evaluateSimulatedGamingRecognitionCounts(List<Integer> trainingIds, List<Integer> withinTaskWorkerIds,
                                                           OptimizeType type, boolean isStrict) {
        ArrayList[] allChosenTaskNatures = new ArrayList[rawLogs.get(0).taskNatures.size()];
        ArrayList[] effortUpperLimits = new ArrayList[rawLogs.get(0).userEfforts.size()];
        ArrayList[] effortLowerLimits = new ArrayList[rawLogs.get(0).userEfforts.size()];
        List<String> maliciousWorkers = new ArrayList<>();
        scanMalicious(trainingIds);
        extractTrainingDataSet(trainingIds, rawLogs, allChosenTaskNatures, effortUpperLimits, effortLowerLimits,
                MAX_WORKER_PER_TASK, maliciousWorkers);
        double[][] convertedEffortUpperLimits = convert(effortUpperLimits);
        double[][] convertedEffortLowerLimits = convert(effortLowerLimits);
        double[][] bestModelFitParams = findBestIntervalParams(allChosenTaskNatures, convertedEffortUpperLimits,
                convertedEffortLowerLimits, trainingIds, type, isStrict);
        return evaluateGaming(trainingIds, false, bestModelFitParams, isStrict);
    }

    /**
     * Evaluates the recognition rate for simulated gaming entries.
     */
    protected double evaluateGaming(List<Integer> trainingTaskids, boolean isTraining,
                                    double[][] regressionParams, boolean isStrict) {
        int[] simuRecogResults = new int[2]; //[0]: recognized, [1]; not recognized
        for (int i = 0; i < rawLogs.size(); i += MAX_WORKER_PER_TASK) {
            RawData rawData = rawLogs.get(i);
            List<Double> taskNatures = rawData.taskNatures;
            double[] userEffortThresholds = computeExpectedValueWithRegressionParams(taskNatures, regressionParams);
            int taskId = i / MAX_WORKER_PER_TASK;
            for (int j = 0; j < MAX_WORKER_PER_TASK; j++) {
                if (isTraining && !trainingTaskids.contains(taskId)) {
                    continue;
                }
                if (!isTraining && trainingTaskids.contains(taskId)) {
                    continue;
                }
                RawData data = rawLogs.get(i + j);
                if (data.simulated && data.quality < 0) {
                    if (!inWorkerBlackList(data.workerId)
                            && isPassing(isStrict, data.userEfforts, userEffortThresholds)) {
                        simuRecogResults[1]++;
                    } else {
                        simuRecogResults[0]++;
                    }
                }
            }
        }
        return simuRecogResults[0] / (double) (simuRecogResults[1] + simuRecogResults[0]);
    }
}
